package kuzha.packagemanager;
import java.io.File;
import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public class JarArchive extends Archive {
	private final File zipfile;
	private final Map<String, Reference<Set<String>>> entry = new HashMap<String, Reference<Set<String>>>();
	
	public JarArchive(File zipfile) {
		this.zipfile = zipfile;
	}
	
	@Override
	public boolean findClass(String[] packagePath, List<String> result) {
		String path = kuzha.Utils.join(".", packagePath);
		Reference<Set<String>> ref = entry.get(path);
		Set<String> clss = ref == null ? null : ref.get();
		if (clss == null) {
			if (!load(path, clss = new HashSet<String>())) {
				return false;
			}
		}
		result.addAll(clss);
		return true;
	}
	
	private boolean load(String packagePath, Set<String> result) {
		ZipFile zf = null;
		try {
			zf = new ZipFile(zipfile);
			Enumeration<? extends ZipEntry> ents = zf.entries();
			while(ents.hasMoreElements()) {
				String name = ents.nextElement().getName();
				if (name.endsWith(".class")) {
					int index = name.lastIndexOf('/');
					String packagename;
					if (0 <= index) {
						packagename = name.substring(0, index).replace('/', '.');
						name = name.substring(index + 1, name.length() - ".class".length());
					}
					else {
						index = name.lastIndexOf('\\');
						if (0 <= index) {
							packagename = name.substring(0, index).replace('/', '.');
							name = name.substring(index + 1, name.length() - ".class".length());
						}
						else {
							packagename = "";
						}
					}
					if (packagePath.equals(packagename)) {
						result.add(name);
					}
				}
			}
			synchronized(entry) {
				entry.put(packagePath, new SoftReference<Set<String>>(result));
			}
		}
		catch(IOException ex) {
			;
		}
		finally {
			try {
				if (zf != null) {
					zf.close();
				}
			}
			catch(IOException ex) {
				;
			}
		}
		return !result.isEmpty();
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((zipfile == null) ? 0 : zipfile.hashCode());
		return result;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final JarArchive other = (JarArchive) obj;
		if (zipfile == null) {
			if (other.zipfile != null)
				return false;
		}
		else if (!zipfile.equals(other.zipfile))
			return false;
		return true;
	}
}
